/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/*********************************************************
* Serialdrv.cpp
* 
* Functions to use RS232 under windows, with VC++
* 
* Opens, Closes, reads(nonblocking) and 
* writes to RS232
*
* Written by Jari Saarinen
* 
* 27.7.2004:  Modified by Antti Maula to be faster and to waste a little less
*             system resources. (Using Windows is waste enough)
*  
**********************************************************/

#include "config.h"
#include <conio.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "serialdrv.h"

#ifdef CAPTURE_DEBUG
#include <time.h>
static FILE *captureDebugOutput = NULL;
#endif

//******************************************************************************

HANDLE openSerial(LPCTSTR sPort, int speed,int stopBits, int Parity)
{
  HANDLE hPort;  
  DCB PortDCB;
  COMMTIMEOUTS CommTimeouts;
  // Open the serial port.
  hPort = CreateFile (sPort, // Pointer to the name of the port
                      GENERIC_READ | GENERIC_WRITE,
                                    // Access (read/write) mode
                      0,            // Share mode
                      NULL,         // Pointer to the security attribute
                      OPEN_EXISTING,// How to open the serial port
                      0,            // Port attributes
                      NULL);        // Handle to port with attribute
                                    // to copy

  // If it fails to open the port, return FALSE.
  if ( hPort == INVALID_HANDLE_VALUE ) 
  {
	  // Could not open the port.
    return FALSE;
  }

  PortDCB.DCBlength = sizeof (DCB);

  // Get the default port setting information.
  GetCommState (hPort, &PortDCB);

  // Change the DCB structure settings.
  PortDCB.BaudRate = speed;              // Current baud
  PortDCB.fBinary = TRUE;               // Binary mode; no EOF check
  //PortDCB.fParity = TRUE;               // Enable parity checking.
  PortDCB.fParity = FALSE;              // NO PARITY check 
  PortDCB.fOutxCtsFlow = FALSE;         // No CTS output flow control
  PortDCB.fOutxDsrFlow = FALSE;         // No DSR output flow control
  PortDCB.fDtrControl = DTR_CONTROL_DISABLE;
                                        // DTR flow control type
  PortDCB.fDsrSensitivity = FALSE;      // DSR sensitivity
  PortDCB.fTXContinueOnXoff = TRUE;     // XOFF continues Tx
  PortDCB.fOutX = FALSE;                // No XON/XOFF out flow control
  PortDCB.fInX = FALSE;                 // No XON/XOFF in flow control
  PortDCB.fErrorChar = FALSE;           // Disable error replacement.
  PortDCB.fNull = FALSE;                // Disable null stripping.
  PortDCB.fRtsControl = RTS_CONTROL_DISABLE;
                                        // RTS flow control
  PortDCB.fAbortOnError = FALSE;        // Do not abort reads/writes on error.
  PortDCB.ByteSize = 8;                 // Number of bits/bytes, 4-8
  PortDCB.Parity = Parity;            // 0-4=no,odd,even,mark,space //EVEN FOR LMS100 AND NO FOR LMS200+
  PortDCB.StopBits = stopBits;        // 0,1,2 = 1, 1.5, 2 // 2 for LMS 100 and 0 for LMS 200+

  // Configure the port according to the specifications of the DCB
  // structure.
  if (!SetCommState (hPort, &PortDCB))
  {
    // Could not configure the serial port.
    return FALSE;
  }

  // Retrieve the time-out parameters for all read and write operations
  // on the port.
  GetCommTimeouts (hPort, &CommTimeouts);

  // Change the COMMTIMEOUTS structure settings.
  CommTimeouts.ReadIntervalTimeout = MAXDWORD;
  CommTimeouts.ReadTotalTimeoutMultiplier = 0;
  CommTimeouts.ReadTotalTimeoutConstant = 0;
  CommTimeouts.WriteTotalTimeoutMultiplier = 10;
  CommTimeouts.WriteTotalTimeoutConstant = 1000;

  // Set the time-out parameters for all read and write operations
  // on the port.
  if (!SetCommTimeouts (hPort, &CommTimeouts))
  {
    // Could not set the time-out parameters.
    return FALSE;
  }

  // Direct the port to perform extended functions SETDTR and SETRTS.
  // SETDTR: Sends the DTR (data-terminal-ready) signal.
  // SETRTS: Sends the RTS (request-to-send) signal.
#ifdef CAPTURE_DEBUG
  EscapeCommFunction (hPort, SETDTR);
  EscapeCommFunction (hPort, SETRTS);
#endif

  return hPort;
}
//******************************************************************************

int writeSerial(HANDLE hPort, unsigned char *outBuff,int size)
{
	DWORD bytes; //how many bytes were written to com port == msgLen
	if (WriteFile(hPort, outBuff, size, &bytes, NULL) == 0) return -1;
	return bytes;
}
//******************************************************************************

BOOL closeSerial(HANDLE hPort)
{
  if (!CloseHandle (hPort))
    return FALSE;
  else  
    return TRUE;
}
//******************************************************************************

int readSerialBlock(HANDLE hPort,unsigned char *inBuff, int msgLen)
{
  BOOL ret;
  DWORD event,bytes;
  int gotBytes = 0;
  
  SetCommMask(hPort, EV_RXCHAR);
  
  do {
    ret = WaitCommEvent(hPort, &event, NULL);
    if (ret == 0) {
      if (GetLastError() != ERROR_IO_PENDING) return -1; // FAILED
    }
    switch(event) {
      case EV_RXCHAR:
        ret = ReadFile(hPort, inBuff + gotBytes, msgLen - gotBytes, &bytes, NULL);
        if (ret == 0) return -1; // FAILED
        gotBytes += bytes;
       break;
      
      default:
        return -1; // INVALID RETURN VALUE      
    }
  } while(gotBytes < msgLen);

#ifdef CAPTURE_DEBUG
  if (captureDebugOutput == NULL) {
     char filename[100];
     sprintf(filename,"captureDebugOutput_%lu.dat", time(NULL));
     captureDebugOutput = fopen(filename, "wb");
  }
  if (captureDebugOutput != NULL) {
     fwrite(inBuff, gotBytes, 1, captureDebugOutput);
     fflush(captureDebugOutput);
  }
#endif

  return gotBytes;
}
//******************************************************************************

int readSerial(HANDLE hPort,unsigned char *inBuff,int msgLen)
{
  DWORD bytes;
	if (ReadFile(hPort,inBuff,msgLen,&bytes,NULL) == 0) return -1;
  return bytes;
}
//******************************************************************************

int clearSerial(HANDLE hPort)
{
  if (PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR) == 0) return -1;
  return 0;
}
